"use strict";

(function(global, namespace) {
  /**
   * @param {(string|string[])} expression
   * @returns {*}
   * 
   * @param {(string|string[])} expression
   * @param {*} value
   * @param {*} [defaultValue]
   */
  var $$data = global.$$data = function() {
    return $$data.value.bind(null, document).apply(null, arguments);
  };
  
  var plugins = $$data.plugins = {};
  var settings = $$data.settings = {};
  settings.nameAttributeName = 'data-name';
  settings.typeAttributeName = 'data-type';
  
  /**
   * 返回表达式 expression 代表的元素的值
   * @param {Element} baseElement
   * @param {(string|string[])} expression
   * @returns {*}
   * 
   * 设置表达式 expression 代表的元素的值
   * @param {Document|Element} baseElement
   * @param {(string|string[])} expression
   * @param {*} value
   * @param {*} [defaultValue]
   */
  $$data.value = function(baseElement, expression, value, defaultValue) {
    $$data.assertArgumentLength(arguments, ['baseElement', 'expression']);
    
    // 获取/设置一组元素的值
    if ((expression instanceof Array) || 
        (expression.length > 0 && expression.charAt(expression.length - 1) === '*')) {
      if (expression instanceof Array && expression.length <= 0) {
        return arguments.length <= 2
          ? {}
          : undefined;
      }
          
      var elements = $$data.getAllElementsByExpression(baseElement, expression);
      
      // 获取一组元素的值
      if (arguments.length <= 2) {
        return $$data.getValues(elements);
      } 
      
      // 设置一组元素的值
      return arguments.length <= 3
        ? $$data.setValues(elements, value)
        : $$data.setValues(elements, value, defaultValue);
    }

    var element = $$data.getFirstElementByExpression(baseElement, expression);
    
    if (element == null) {
      return undefined;
    }
    
    // 获取/设置单个元素的值
    return arguments.length <= 2
      ? $$data.getValue(element)
      : $$data.setValue(element, value);
  };
  
  /**
   * 获取一组元素 elements 的值
   * @param {element[]} elements
   * @returns {Object}
   */
  $$data.getValues = function(elements) {
    $$data.assertArgumentLength(arguments, ['elements']);
    
    var values = {};
    
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      var name = element.getAttribute(settings.nameAttributeName);
      
      values[name] = $$data.getValue(element);
    }
    
    return values;
  };
  
  /**
   * 设置一组元素 elements 的值
   * @param {Element[]} elements
   * @param {*} value
   * @param {*} [defaultValue]
   */
  $$data.setValues = function(elements, value, defaultValue) {
    $$data.assertArgumentLength(arguments, ['elements', 'value']);
    
    if (!(typeof value === 'object' && value !== null)) {
      throw new Error('argument#2 "value" required JSON');
    }
    
    var argumentLength = arguments.length;
    
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      var name = element.getAttribute(settings.nameAttributeName);
      
      if (name in value) {
        $$data.setValue(element, value[name]);
      } else if (argumentLength >= 3) {
        $$data.setValue(element, defaultValue);
      }
    }
  };
  
  /**
   * 获取元素 element 的值
   * @param {Element} element
   * @returns {*}
   */
  $$data.getValue = function(element) {
    $$data.assertArgumentLength(arguments, ['element']);
    
    var dataType = element.getAttribute(settings.typeAttributeName);
    var tagName = element.tagName.toLowerCase();
    
    if (!dataType) {
      return plugins['$' + tagName] 
        ? plugins['$' + tagName].getValue(element) 
        : $$data.doGetValue(element);
    }
    
    var plugin = plugins[dataType];
    
    if (plugin == null) {
      throw new Error('not register ' + settings.typeAttributeName + ' "' + dataType + '"');
    }
    
    if (plugin.getValue == null) {
      throw new Error('not define "getValue" for ' + settings.typeAttributeName + ' "' + dataType + '"');
    }
    
    return plugin.getValue(element);
  };
  
  /**
   * 设置元素 element 的值
   * @param {Element} element
   * @param {*} value
   */
  $$data.setValue = function(element, value) {
    $$data.assertArgumentLength(arguments, ['element', 'value']);
    
    var dataType = element.getAttribute(settings.typeAttributeName);
    var tagName = element.tagName.toLowerCase();
    
    if (!dataType) {
      if (plugins['$' + tagName]) {
        plugins['$' + tagName].setValue(element, value);
      } else {
        $$data.doSetValue(element, value);
      }
      
      return;
    }
    
   var plugin = plugins[dataType];
    
    if (plugin == null) {
      throw new Error('not register ' + settings.typeAttributeName + ' "' + dataType + '"');
    }
    
    if (plugin.setValue == null) {
      throw new Error('not define "setValue" for ' + settings.typeAttributeName + ' "' + dataType + '"');
    }
    
    plugin.setValue(element, value);
  };
  
  /**
   * 获取元素 element 的值
   * @param {Element} element
   * @returns {*}
   */
  $$data.doGetValue = function(element) {
    $$data.assertArgumentLength(arguments, ['element']);

    var tagName = element.tagName.toLowerCase();
    
    // 不是表单元素（不包括 <button> 标签）
    if ('<input><select><textarea>'.indexOf('<' + tagName + '>') < 0) {
      return element.innerHTML;
    }
    
    // 下拉框
    if (tagName === 'select') {
      return $$data.getSelectedOption(element);
    }
    
    var inputType = element.getAttribute('type');
    
    return (inputType === 'radio' || inputType === 'checkbox')
      ? element.checked
      : element.value;
  };
  
  /**
   * 设置元素 element 的值
   * @param {Element} element
   * @param {*} value
   */
  $$data.doSetValue = function(element, value) {
    $$data.assertArgumentLength(arguments, ['element', 'value']);
    
    var tagName = element.tagName.toLowerCase();
    
    // 不是表单元素（不包括 <button> 标签）
    if ('<input><select><textarea>'.indexOf('<' + tagName + '>') < 0) {
      element.innerHTML = (value != null ? value : '');
      return;
    }
    
    // 下拉框
    if (tagName === 'select') {
      $$data.setSelectedOption(element, value);
      return;
    }
    
    var inputType = element.getAttribute('type');
    
    if (inputType === 'radio' || inputType === 'checkbox') {
      element.checked = (value === true) ? true : false;
    } else {
      element.value = (value != null ? value : '');
    }
  };
  
  /**
   * 获取选择列表 element 选中的选项
   * @param {Element} element
   * @returns {*}
   */
  $$data.getSelectedOption = function(element) {
    $$data.assertArgumentLength(arguments, ['element']);
    
    // 单选下拉框
    if (!element.multiple) {
      var selectedIndex = element.selectedIndex;
      
      return (selectedIndex !== -1)
        ? element.item(selectedIndex).value
        : null;
    }
    
    // 多选下拉框
    var selectedValues = [];
    
    for (var i = 0; i < element.length; i++) {
      var option = element.item(i);
      
      if (option.selected) {
        selectedValues.push(option.value);
      }
    }
    
    return selectedValues;
  };
  
    
  /**
   * 设置选择列表 element 选中的选项
   * @param {Element} element
   * @param {*} value
   */
  $$data.setSelectedOption = function(element, value) {
    $$data.assertArgumentLength(arguments, ['element', 'value']);
    
    // 单选下拉框
    if (!element.multiple) {
      element.value = value;
      return;
    }
    
    // 多选下拉框
    for (var i = 0; i < element.length; i++) {
      var option = element.item(i);
      var selected = false;
      
      if (value instanceof Array) {
        for (var j = 0; j < value.length; j++) {
          if (value[j] == option.value) {
            selected = true;
            break;
          }
        }
      }
      
      option.selected = selected;
    }
  };
  
  /**
   * 返回元素 baseElement 中匹配表达式 expression 的第一个元素
   * @param {Document|Element} baseElement
   * @param {(string|string[])} expression
   * @returns {Element}
   */
  $$data.getFirstElementByExpression = function(baseElement, expression) {
    $$data.assertArgumentLength(arguments, ['baseElement', 'expression']);

    if ((typeof expression !== 'string') && !(expression instanceof Array)) {
      throw new Error('argument#2 "expression" required string or Array');
    }
    
    var selector = $$data.expressionToSelector(expression);
    return baseElement.querySelector(selector);
  };
  
  /**
   * 返回元素 baseElement 中匹配表达式 expression 的元素列表
   * @param {Document|Element} baseElement
   * @param {(string|string[])} expression
   * @returns {Element[]}
   */
  $$data.getAllElementsByExpression = function(baseElement, expression) {
    $$data.assertArgumentLength(arguments, ['baseElement', 'expression']);
    
    if ((typeof expression !== 'string') && !(expression instanceof Array)) {
      throw new Error('argument#2 "expression" required string or Array');
    }
    
    var selector = $$data.expressionToSelector(expression);
    return baseElement.querySelectorAll(selector);
  };
  
  /**
   * 把表达式 expression 转换成 CSS 选择器
   * @param {string|string[]} expression
   * @returns {string}
   * 
   * 转换的规则
   * "a"              => "[data-name="a"]"
   * "a*"             => "[data-name^="a"]"
   * "*"              => "[data-name]"
   * ["a", "a*", "*"] => "[data-name="a"],[data-name^="a"],[data-name]"
   */
  $$data.expressionToSelector = function(expression) {
    $$data.assertArgumentLength(arguments, ['expression']);
  
    if ((typeof expression !== 'string') && !(expression instanceof Array)) {
      throw new Error('argument#1 "expression" required string or Array');
    }
    
    var selectors = [];
    var expressionArr = (expression instanceof Array) 
      ? expression : [expression];
      
    for (var i = 0; i < expressionArr.length; i++) {
      var expressionStr = expressionArr[i];
      var selector;
      
      if (expressionStr === '*') { // "*"
        selector = '[' + settings.nameAttributeName + ']';
      } else if (expressionStr.length > 1 && expressionStr.charAt(expressionStr.length - 1) === '*') { // "a*"
        selector = '[' + settings.nameAttributeName + '^="' + expressionStr.slice(0, -1) + '"]';
      } else { // "a"
        selector = '[' + settings.nameAttributeName + '="' + expressionStr + '"]';
      }
      
      selectors.push(selector);
    }
    
    return selectors.join(',');
  };
  
  /**
   * 给对象 obj 所有属性名加上 prefix 前缀(不修改参数 obj，返回一个新的对象)
   * @param {Object} obj
   * @param {string} prefix
   * @returns {Object}
   */
  $$data.prefix = function(obj, prefix) {
    $$data.assertArgumentLength(arguments, ['obj', 'prefix']);
  
    if (typeof prefix !== 'string') {
      throw new Error('argument#2 "prefix" required string');
    }
    
    if (obj === null || typeof obj !== 'object') {
      return obj;
    }
    
    var newObj = {};
    
    for (var key in obj) {
      newObj[prefix + key] = obj[key];
    }
    
    return newObj;
  };
  
  /**
   * 去掉对象 obj 所有属性名的 prefix 前缀(不修改参数 obj，返回一个新的对象)
   * @param {Object} obj
   * @param {string} prefix
   * @returns {Object}
   */
  $$data.unprefix = function(obj, prefix) {
    $$data.assertArgumentLength(arguments, ['obj', 'prefix']);
  
    if (typeof prefix !== 'string') {
      throw new Error('argument#2 "prefix" required string');
    }
    
    if (obj === null || typeof obj !== 'object') {
      return obj;
    }
    
    var newKey;
    var newObj = {};
    
    for (var key in obj) {
      newKey = key.indexOf(prefix) === 0
        ? key.substring(prefix.length)
        : key;
        
      newObj[newKey] = obj[key];
    }
    
    return newObj;
  };
  
  /**
   * 检查参数的个数
   * @param {Arguments} actualArguments
   * @param {string[]} requiredArguments
   */
  $$data.assertArgumentLength = function(actualArguments, requiredArguments) {
    if (actualArguments.length >= requiredArguments.length) {
      return;
    }
    
    var strArr = [];
    
    for (var i = actualArguments.length; i < requiredArguments.length; i++) {
      strArr.push('argument#' + (i+1) + ' "' + requiredArguments[i] + '"');
    }

    throw new Error('missing ' + strArr.join(' and '));
  };

  global[namespace] = $$data; // 绑定命名空间
})(window, '_');